package com.keensense.vcm;

import static com.keensense.vcm.common.utils.LogUtils.logException;

import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import com.keensense.vcm.common.auth.CredentialsProvider;
import com.keensense.vcm.common.comm.DefaultServiceClient;
import com.keensense.vcm.common.comm.ServiceClient;
import com.keensense.vcm.internal.*;
import com.keensense.vcm.model.*;
import com.keensense.vcm.model.enums.LanguageEnum;
import com.keensense.vcm.model.enums.TypeCodeEnum;

/**
 * @author lanjian
 * @date 2020/4/28 9:51 上午
 */
public class VCMClient implements VCM {

    /**
     * The default service client
     */
    private ServiceClient serviceClient;

    /**
     * The valid endpoint for accessing to OSS services
     */
    private URI endpoint;

    /**
     * The default credentials provider
     */
    private CredentialsProvider credsProvider;

    private IntelligentSearchOperation intelligentSearchOperation;

    private LoginOperation loginOperation;

    private VehicleAnalysisOperation vehicleAnalysisOperation;

    private ProfileOperation profileOperation;

    private ProfileLibraryOperation profileLibraryOperation;

    private IntelligentAnalysisTaskOperation intelligentAnalysisTaskOperation;

    private AlarmOperation alarmOperation;

    private SuspectOperation suspectOperation;

    private CameraOperation cameraOperation;

    private StaticLibrariesOperation staticLibrariesOperation;

    private SubscriptionOperation subscriptionOperation;

    private DataDictionaryOperation dataDictionaryOperation;

    private SpatiotemporalOperation spatiotemporalOperation;

    public VCMClient(String endpoint, CredentialsProvider credsProvider, ClientConfiguration config) {
        this.credsProvider = credsProvider;
        config = config == null ? new ClientConfiguration() : config;
        this.serviceClient = new DefaultServiceClient(config);
        initOperations();
        setEndpoint(endpoint);
    }

    /**
     * 初始化
     */
    private void initOperations() {
        this.intelligentSearchOperation = new IntelligentSearchOperation(this.serviceClient, this.credsProvider);
        this.loginOperation = new LoginOperation(this.serviceClient, this.credsProvider);
        this.vehicleAnalysisOperation = new VehicleAnalysisOperation(this.serviceClient, this.credsProvider);
        this.profileOperation = new ProfileOperation(this.serviceClient, this.credsProvider);
        this.intelligentAnalysisTaskOperation =
                new IntelligentAnalysisTaskOperation(this.serviceClient, this.credsProvider);
        this.profileLibraryOperation = new ProfileLibraryOperation(this.serviceClient, this.credsProvider);
        this.alarmOperation = new AlarmOperation(this.serviceClient, this.credsProvider);
        this.suspectOperation = new SuspectOperation(this.serviceClient, this.credsProvider);
        this.cameraOperation = new CameraOperation(this.serviceClient, this.credsProvider);
        this.staticLibrariesOperation = new StaticLibrariesOperation(this.serviceClient, this.credsProvider);
        this.subscriptionOperation = new SubscriptionOperation(this.serviceClient, this.credsProvider);
        this.dataDictionaryOperation = new DataDictionaryOperation(this.serviceClient, this.credsProvider);
        this.spatiotemporalOperation = new SpatiotemporalOperation(this.serviceClient, this.credsProvider);
    }

    public synchronized void setEndpoint(String endpoint) {
        URI uri = toURI(endpoint);
        this.endpoint = uri;
        if (isIpOrLocalhost(uri)) {
            serviceClient.getClientConfiguration().setSLDEnabled(true);
        }
        this.intelligentSearchOperation.setEndpoint(uri);
        this.loginOperation.setEndpoint(uri);
        this.vehicleAnalysisOperation.setEndpoint(uri);
        this.profileOperation.setEndpoint(uri);
        this.intelligentAnalysisTaskOperation.setEndpoint(uri);
        this.profileLibraryOperation.setEndpoint(uri);
        this.alarmOperation.setEndpoint(uri);
        this.suspectOperation.setEndpoint(uri);
        this.cameraOperation.setEndpoint(uri);
        this.staticLibrariesOperation.setEndpoint(uri);
        this.subscriptionOperation.setEndpoint(uri);
        this.dataDictionaryOperation.setEndpoint(uri);
        this.spatiotemporalOperation.setEndpoint(uri);
    }

    private boolean isIpOrLocalhost(URI uri) {
        if (uri.getHost().equals("localhost")) {
            return true;
        }

        InetAddress ia;
        try {
            ia = InetAddress.getByName(uri.getHost());
        } catch (UnknownHostException e) {
            return false;
        }

        if (uri.getHost().equals(ia.getHostAddress())) {
            return true;
        }

        return false;
    }

    private URI toURI(String endpoint) throws IllegalArgumentException {
        if (!endpoint.contains("://")) {
            ClientConfiguration conf = this.serviceClient.getClientConfiguration();
            endpoint = conf.getProtocol().toString() + "://" + endpoint;
        }

        try {
            return new URI(endpoint);
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException(e);
        }
    }

    @Override
    public void shutDown() {
        try {
            serviceClient.shutdown();
        } catch (Exception e) {
            logException("shutdown throw exception: ", e);
        }
    }

    /**
     * 登录
     */
    @Override
    public VCMResponse login() {
        return loginOperation.login();
    }

    /**
     * 过人过脸搜索
     *
     * @param request
     * @return
     */
    @Override
    public PeopleFaceSearchResponse searchPeopleFace(PeopleFaceSearchRequest request) {
        return intelligentSearchOperation.searchPeopleFace(request);
    }

    /**
     * 查询单动态库N中人脸出现的频次，按照频次降序排序。
     *
     * @param nvNRequest
     * @return
     */
    @Override
    public NvNResponse faceMatchNvN(NvNRequest nvNRequest) {
        return intelligentSearchOperation.faceMatchNvN(nvNRequest);
    }

    /**
     * 根据taskId查询nvn结果集
     *
     * @param taskId
     * @return
     */
    @Override
    public NvNResponse getFaceMatchNvnResult(String taskId) {
        return intelligentSearchOperation.getFaceMatchNvN(taskId);
    }

    /**
     * 根据特征搜索车辆
     *
     * @param request
     * @return
     */
    @Override
    public VehicleSearchResponse searchVehicle(VehicleSearchRequest request) {
        return vehicleAnalysisOperation.searchVehicle(request);
    }

    /**
     * 根据图片搜索车辆
     *
     * @param request
     * @return
     */
    @Override
    public VehicleSearchResponse searchVehicleByPhoto(VehicleSearchByPhotoRequest request) {
        return vehicleAnalysisOperation.searchVehicleByPhoto(request);
    }

    /**
     * 1、创建智能分析任务。 2、不建议同时启动车牌识别和车辆识别任务。 3、VCM北向接口请求中可能新增可选字段，或响应中增加字段。 ISV客户端需支持过滤不使用的新增字段，进行兼容性处理。
     * 4、对同一摄像机或视频文件，不建议同时创建多个分析内容相近的的智能分析任务，例如车辆识别和人车混合结构化智能分析任务、人体识别和人车混合结构化智能分析任务。
     * 5、如果用户需要对不同算法的效果进行比较，可以对同一摄像机或视频文件，使用不同算法同时创建相同类型的智能分析任务。
     *
     * @param startIntelligentAnalysisRequest
     * @return
     */
    @Override
    public VCMResponse startIntelligentAnalysis(StartIntelligentAnalysisRequest startIntelligentAnalysisRequest) {
        return intelligentAnalysisTaskOperation.startIntelligentAnalysis(startIntelligentAnalysisRequest);
    }

    /**
     * 1、查询智能分析任务列表。 2、支持域间任务检索，可根据任务创建域和任务执行域查询智能分析任务。 3、VCM北向接口请求中可能新增可选字段，或响应中增加字段。 ISV客户端需支持过滤不使用的新增字段，进行兼容性处理。
     *
     * @param getIntelligentAnalysisListRequest
     * @return
     */
    @Override
    public GetIntelligentAnalysisListResponse
    getIntelligentAnalysisList(GetIntelligentAnalysisListRequest getIntelligentAnalysisListRequest) {
        return intelligentAnalysisTaskOperation.getIntelligentAnalysisList(getIntelligentAnalysisListRequest);
    }

    /**
     * 指定任务编码删除智能分析任务。
     *
     * @param taskId
     * @return
     */
    @Override
    public VCMResponse deleteIntelligentAnalysis(String taskId) {
        return intelligentAnalysisTaskOperation.deleteIntelligentAnalysis(taskId);
    }

    /**
     * <p>
     * 1、增加人员档案，支持将静态库迁移到档案库。
     * </p>
     * <p>
     * 2、定时扫描静态库，将该静态库新增人员创建成档案
     * </p>
     *
     * @param addProfileRequest
     */
    @Override
    public VCMResponse addProfile(AddProfileRequest addProfileRequest) {
        return profileOperation.createByLibrary(addProfileRequest);
    }

    /**
     * <p>
     * 1、支持档案数据迁移，可以将某些过脸数据迁移归入某个档案
     * </p>
     *
     * @param migrateTrajectoryRequest
     * @return
     */
    @Override
    public VCMResponse updateProfile(MigrateTrajectoryRequest migrateTrajectoryRequest) {
        return profileOperation.migrateTrajectory(migrateTrajectoryRequest);
    }

    /**
     * <p>
     * 1、合并人员档案，支持多个档案合并，支持无名档合并，无名档合入有名档。
     * </p>
     *
     * @param mergeProfileRequest
     * @return
     */
    @Override
    public VCMResponse mergeProfile(MergeProfileRequest mergeProfileRequest) {
        return profileOperation.mergeProfile(mergeProfileRequest);
    }

    /**
     * <p>
     * 1、输入人员档案id的情况下会忽略其他信息。
     * </p>
     * 2、人员档案检索，以图搜档的情况下会忽略所有结构化信息。
     *
     * @param batchQueryProfileRequest
     * @return
     */
    @Override
    public BatchQueryProfileResponse queryProfile(BatchQueryProfileRequest batchQueryProfileRequest) {
        return profileOperation.batchQueryProfile(batchQueryProfileRequest);
    }

    /**
     * 查询所有档案库
     *
     * @return
     */
    @Override
    public QueryProfileLibrariesResponse queryAllProfileLibrary() {
        return profileLibraryOperation.queryAll();
    }

    /**
     * 查询人员档案库
     *
     * @param queryProfileLibrariesRequest
     * @return
     */
    @Override
    public QueryProfileLibrariesResponse
    queryProfileLibraries(QueryProfileLibrariesRequest queryProfileLibrariesRequest) {
        return profileLibraryOperation.queryProfileLibraries(queryProfileLibrariesRequest);
    }


    /**
     * 根据id查询人员档案库
     *
     * @param pId
     * @return
     */
    @Override
    public QueryProfileLibrariesResponse queryProfileLibraryById(String pId) {
        return profileLibraryOperation.queryById(pId);
    }

    /**
     * 更新人员档案库，支持更新名字、描述、tag
     *
     * @param updateProfileLibrariesRequest
     * @return
     */
    @Override
    public VCMResponse updateProfileLibraries(UpdateProfileLibrariesRequest updateProfileLibrariesRequest) {
        return profileLibraryOperation.updateProfileLibraries(updateProfileLibrariesRequest);
    }

    /**
     * <p>
     * <删除人员档案库/p>
     * <p>
     * 1、删除普通档案库，不删除档案，只删除档案和档案库的映射关系。
     * </p>
     * <p>
     * 2、删除有名档/无名档，直接删除档案，包括档案中的数据。
     * </p>
     *
     * @param id
     * @return
     */
    @Override
    public VCMResponse deleteProfileLibraries(String id) {
        return profileLibraryOperation.deleteProfileLibraries(id);
    }

    /**
     * 增加人员档案库，如重点人员档案库、高频档案库等，档案库的个数限制128
     *
     * @param addProfileLibrariesRequest
     * @return
     */
    @Override
    public AddProfileLibrariesResponse addProfileLibraries(AddProfileLibrariesRequest addProfileLibrariesRequest) {
        return profileLibraryOperation.addProfileLibraries(addProfileLibrariesRequest);
    }

    /**
     * <p>
     * 人员档案删除
     * </p>
     * <p>
     * 1、删除档案后会自动删除静态库人员
     * </p>
     * <p>
     * 2、支持删除无名档案。
     * </p>
     * <p>
     * 3、若删除静态库数据，则档案变更为无名档案。
     * </p>
     *
     * @param dIds
     * @return
     */
    @Override
    public VCMResponse deleteProfile(String dIds) {
        return profileOperation.deleteProfile(dIds);
    }


    /**
     * 根据图片搜索档案
     *
     * @return
     */
    @Override
    public BatchQueryProfileResponse queryByPicture(QueryProfileByPictureRequest queryProfileByPictureRequest) {
        return profileOperation.queryByPicture(queryProfileByPictureRequest);
    }

    /**
     * 人员档案统计接口，查询接口的两个字段互斥，不能同时填写。
     *
     * @return
     */
    @Override
    public StatisticArchivesResponse statisticProfile() {
        return profileOperation.statisticProfile();
    }

    /**
     * 手动添加人员档案
     * <p>
     * 1、添加档案必须指定静态库，档案会同步添加到静态库中。
     * </p>
     * <p>
     * 2、添加档案必须指定已经建档的静态库。
     * </p>
     *
     * @param manualCreateProfileRequest
     * @return
     */
    @Override
    public ManualCreateProfileResponse manualCreateProfile(ManualCreateProfileRequest manualCreateProfileRequest) {
        return profileOperation.manualCreateProfile(manualCreateProfileRequest);
    }

    /**
     * 根据档案id查询档案
     *
     * @param id
     * @return
     */
    @Override
    public BatchQueryProfileResponse queryProfileById(String id) {
        return profileOperation.queryProfileById(id);
    }

    /**
     * 查询档案过脸信息
     *
     * @param profileQueryFacesRequest
     * @return
     */
    @Override
    public ProfileQueryFacesResponse queryProfileFaces(ProfileQueryFacesRequest profileQueryFacesRequest) {
        return profileOperation.profileQueryFaces(profileQueryFacesRequest);
    }

    /**
     * 查询未归档过脸信息
     *
     * @param queryUnFiledFaceRequest
     * @return
     */
    @Override
    public ProfileQueryFacesResponse queryUnFiledFaces(QueryUnFiledFaceRequest queryUnFiledFaceRequest) {
        return profileOperation.queryUnFiledFaces(queryUnFiledFaceRequest);
    }

    /**
     * 更新档案
     *
     * @param updateProfileRequest
     * @return
     */
    @Override
    public VCMResponse updateProfile(UpdateProfileRequest updateProfileRequest) {
        return profileOperation.updateProfile(updateProfileRequest);
    }

    /**
     * 将档案从档案库中移除
     *
     * @param operationProfileFromLibraryRequest
     * @return
     */
    @Override
    public VCMResponse delProfileFromLibrary(OperationProfileFromLibraryRequest operationProfileFromLibraryRequest) {
        return profileOperation.delProfileFromLibrary(operationProfileFromLibraryRequest);
    }

    /**
     * 添加人像到档案库
     *
     * @param operationProfileFromLibraryRequest
     * @return
     */
    @Override
    public VCMResponse addProfileToLibrary(OperationProfileFromLibraryRequest operationProfileFromLibraryRequest) {
        return profileOperation.addProfileToLibrary(operationProfileFromLibraryRequest);
    }

    /**
     * 批量添加人像到档案库
     *
     * @param batchAddProfileToLibraryRequest
     * @return
     */
    @Override
    public VCMResponse batchAddProfileToLibrary(BatchAddProfileToLibraryRequest batchAddProfileToLibraryRequest) {
        return profileOperation.batchAddProfileToLibrary(batchAddProfileToLibraryRequest);
    }


    /**
     * <P>
     * 1、查询历史告警信息
     * </P>
     * <p>
     * 2、新增可根据布控名单类型、算法编码或者命中类型查询告警信息
     * </p>
     * <p>
     * 3、支持pageSize=0，当pageSize=0是只返回总数，且是精确的
     * </p>
     * <p>
     * 4、新增返回摄像机的经纬度信息
     * </p>
     * <p>
     * 5、新增返回布控Id的列表
     * </p>
     * <p>
     * 6、VCM北向接口请求中可能新增可选字段，或响应中增加字段。 ISV客户端需支持过滤不使用的新增字段，进行兼容性处理
     * </p>
     * <p>
     * 7、接口响应增加url字段，用于下载布控名单图片
     * </p>
     * <p>
     * 8、接口响应增加suspectPictureId字段，表示布控图片id
     * </p>
     *
     * @param searchAlarmRequest
     * @return
     */
    @Override
    public SearchAlarmResponse searchAlarm(SearchAlarmRequest searchAlarmRequest) {
        return alarmOperation.searchAlarm(searchAlarmRequest);
    }

    /**
     * 1、查询布控任务。
     * <P>
     * 2、VCM北向接口请求中可能新增可选字段，或响应中增加字段。 ISV客户端需支持过滤不使用的新增字段，进行兼容性处理。
     * </p>
     *
     * @param searchSuspectTaskRequest
     * @return
     */
    @Override
    public SearchSuspectTaskResponse searchSuspectTask(SearchSuspectTaskRequest searchSuspectTaskRequest) {
        return suspectOperation.searchSuspectTask(searchSuspectTaskRequest);
    }


    /**
     * 1:1人脸比对。image2 为对比基准图片。
     *
     * @param oneVOneRequest
     * @return
     */
    @Override
    public OneVOneResponse faceOneVOne(OneVOneRequest oneVOneRequest) {
        return intelligentSearchOperation.faceOneVOne(oneVOneRequest);
    }

    /**
     * 根据过脸记录Id查询过脸详细信息
     *
     * @param recordId
     * @return
     */
    @Override
    public SnapInfosResponse getSnapInfos(String... recordId) {
        SnapInfosRequest snapInfosRequest = new SnapInfosRequest();
        List<String> recordIds = new ArrayList<>(recordId.length);
        for (String id : recordId) {
            recordIds.add(id);
        }
        snapInfosRequest.setRecordIds(recordIds);
        return intelligentSearchOperation.getFaceSnap(snapInfosRequest);
    }

    /**
     * 获取相机列表
     *
     * @param request
     * @return
     */
    @Override
    public ListCameraResponse listCamera(ListCameraRequest request) {
        return cameraOperation.listCamera(request);
    }

    /**
     * 静态库查询人员信息
     *
     * @param request
     * @return
     */
    @Override
    public GetPeoplesResponse getStaticLibrariesPeoples(GetPeoplesRequest request) {
        return staticLibrariesOperation.getPeoples(request);
    }

    /**
     * 静态库查询
     *
     * @param request
     * @return
     */
    @Override
    public ListStaticLibrariesResponse listStaticLibraries(ListStaticLibrariesRequest request) {
        return staticLibrariesOperation.listStaticLibraries(request);
    }


    @Override
    public SubscriptionResponse subscription(SubscriptionRequest subscriptionRequest) {
        return subscriptionOperation.subscription(subscriptionRequest);
    }

    @Override
    public SearchSubscriptionResponse searchSubscription(SearchSubscriptionRequest searchSubscriptionRequest) {
        return subscriptionOperation.searchSubscription(searchSubscriptionRequest);
    }

    @Override
    public VCMResponse deleteSubscription(DeleteSubscriptionRequest deleteSubscriptionRequest) {
        return subscriptionOperation.deleteSubscription(deleteSubscriptionRequest);
    }

    @Override
    public GetDataDictionaryResponse getDataDictionary(LanguageEnum language, TypeCodeEnum typeCode) {
        return dataDictionaryOperation.getDataDictionary(language, typeCode);
    }

    /**
     * 统计同时在2个时空下的出现的人员和人员次数
     *
     * @param spatiotemporalRequest
     * @return
     */
    @Override
    public SpatiotemporalResponse collide(SpatiotemporalRequest spatiotemporalRequest) {
        return spatiotemporalOperation.collide(spatiotemporalRequest);
    }

    /**
     * 统计在单时空下的出现的人员和人员次数
     *
     * @param spatiotemporalRequest
     * @return
     */
    @Override
    public SpatiotemporalResponse calcFrequency(SpatiotemporalRequest spatiotemporalRequest) {
        return spatiotemporalOperation.calcFrequency(spatiotemporalRequest);
    }

    /**
     * 获取时空分析任务状态
     *
     * @param taskId
     * @return
     */
    @Override
    public SpatiotemporalResponse getTaskResult(String taskId) {
        return spatiotemporalOperation.getTaskResult(taskId);
    }

}
